package com.xzy.sms.core.service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xzy.sms.core.adapter.SendSmsAdapter;
import com.xzy.sms.core.entity.SendContext;
import com.xzy.sms.core.utils.EhcacheUtil;
import com.xzy.sms.entity.SendSmsRequest;
import com.xzy.sms.exception.SendSmsException;
import com.xzy.sms.service.SendSmsService;
import com.xzy.sms.utils.SmsUtils;
import com.xzy.sms.web.domain.entity.Application;
import com.xzy.sms.web.domain.entity.ChannelConfig;
import com.xzy.sms.web.domain.entity.SmsHistory;
import com.xzy.sms.web.domain.entity.Template;
import com.xzy.sms.web.domain.service.ApplicationService;
import com.xzy.sms.web.domain.service.ChannelConfigService;
import com.xzy.sms.web.domain.service.SmsHistoryService;
import com.xzy.sms.web.domain.service.TemplateService;

/**
 * @author huchangjiang
 * @since 1.0
 * @version 2016年1月8日 huchangjiang
 */
@Service
public class SendSmsServiceImpl implements SendSmsService {
    
    /**
     * 日志对象
     */
    protected Logger logger = LoggerFactory.getLogger(getClass());
    
    /** FIXME */
    private static boolean SIGN_VALID = true;
    
    /** FIXME */
    @Autowired
    private SmsHistoryService smsHistoryService;
    
    /** 适配器缓存 */
    private Map<String, SendSmsAdapter> mapClass2Adapter = new ConcurrentHashMap<>();
    
    /** FIXME */
    @Autowired
    private ExceptionProcesser exceptionProcesser;
    
    @Autowired
    private ApplicationService applicationService;
    @Autowired
    private ChannelConfigService channelConfigService;
    @Autowired
    private TemplateService templateService;
    
    /**
     * 发送短信
     * 
     * @param context sms
     * @throws SendSmsException x
     */
    @Override
    public void sendSms(SendSmsRequest request) throws SendSmsException {
        boolean result = false;
        // 必要参数校验
        validRequest(request);
        
        /* 以下是业务检查 */
        String applicationId = request.getApplicationId();
        Application application = applicationService.getOne(applicationId);
        if (application == null) {
            throwSendSmsException("对应的应用不存在 applicationId=" + applicationId);
        } else if (StringUtils.isBlank(application.getChannelId())) {
            throwSendSmsException("没有绑定发送通道 applicationId=" + applicationId);
        }
        
        /* 安全性检查，检查签名正确性 */
        if (SIGN_VALID && !validSign(request, application.getSecretKey())) {
            throwSendSmsException("参数sign不正确");
        }
        
        String channelId = application.getChannelId();
        ChannelConfig channelConfig = channelConfigService.getOne(channelId);
        if (channelConfig == null) {
            throwSendSmsException("没有获取到应用对应的通道配置，applicationId=" + applicationId + ";channelId=" + channelId);
        } else if (StringUtils.isBlank(channelConfig.getSendAdapterClass())) {
            throwSendSmsException("发送通道没有配置适配器类参数，channelId=" + channelId);
        }
        
        // 获取适配器类
        SendSmsAdapter adapter = getSendAdapater(channelConfig);
        if (adapter == null) {
            throwSendSmsException("发送通道适配器类参数不正确，channelId=" + channelId);
        }
        
        // 参数转换
        SendContext context = new SendContext(request);
        context.setApplication(application);
        context.setChannelConfig(channelConfig);
        
        // 获取模板
        if (StringUtils.isBlank(context.getContent())) {
            Template template = null;
            String templateId = request.getTemplateId();
            String code = request.getTemplateCode();
            if (StringUtils.isBlank(templateId)) {
            	template = templateService.findByUserIdAndCode(context.getUserId(), code);
            } else {
                template = templateService.getOne(templateId);
                if (template != null) {
                    // 由于JPA的懒加载问题，这里需要调用一次
                    template.getUser().setUserId(template.getUser().getUserId());
                }
            }
            if (template == null) {
                throwSendSmsException("没有找到对应的模板，templateId=" + templateId + "，templateCode=" + code);
            }
            context.setTemplate(template);
        }
        
        try {
            adapter.sendSms(context);
            result = true;
        } catch (SendSmsException ex) {
            exceptionProcesser.addException(ex);
            logger.error("使用网关发送短信失败", ex);
            throw ex;
        } catch (Exception ex) {
            exceptionProcesser.addException(ex);
            logger.error("使用网关发送短信失败", ex);
            throw new RuntimeException("使用网关发送短信失败," + ex.getMessage(), ex);
        } finally {
            
            // 写入记录
            try {
                SmsHistory sh = SmsHistory.fromSendSmsContext(context);
                sh.setSendResult(result ? 1 : 0);
                smsHistoryService.save(sh);
            } catch (Exception ex) {
                logger.error("保存短信发送记录时异常", ex);
                exceptionProcesser.addException(ex);
            }
        }
    }
    
    /**
     * FIXME 方法注释信息(此标记由Eclipse自动生成,请填写注释信息删除此标记)
     *
     * @param ssr x
     * @throws SendSmsException x
     */
    private void validRequest(SendSmsRequest ssr) throws SendSmsException {
        if (StringUtils.isBlank(ssr.getApplicationId())) {
            throwSendSmsException("缺少applicationId参数");
        } else if (StringUtils.isBlank(ssr.getMobile())) {
            throwSendSmsException("缺少mobile参数");
        } else if (StringUtils.isNotBlank(ssr.getContent()) && StringUtils.isNotBlank(ssr.getTemplateId())
            && StringUtils.isNotBlank(ssr.getTemplateCode())) {
            throwSendSmsException("缺少content,templateId,templateCode参数其中一个");
        } else if (StringUtils.isBlank(ssr.getSign())) {
            throwSendSmsException("缺少sign");
        }
        
    }
    
    /**
     * FIXME 方法注释信息(此标记由Eclipse自动生成,请填写注释信息删除此标记)
     *
     * @param ssr x
     * @param secretKey xx
     * @return x
     */
    private boolean validSign(SendSmsRequest ssr, String secretKey) {
        return StringUtils.equals(SmsUtils.sign(ssr.getRawSign(), secretKey), ssr.getSign());
    }
    
    /**
     * 将配置转换为发送通道
     *
     * @param channelConfig x
     * @return x
     * @throws SendSmsException x
     */
    private SendSmsAdapter getSendAdapater(ChannelConfig channelConfig) throws SendSmsException {
        String adapterClass = channelConfig.getSendAdapterClass();
        if (StringUtils.isBlank(adapterClass)) {
            return null;
        }
        
        SendSmsAdapter adapter = mapClass2Adapter.get(adapterClass);
        
        if (adapter != null) {
            return adapter;
        }
        
        try {
            Class<?> clazz = Class.forName(adapterClass);
            Object obj = clazz.newInstance();
            if (obj == null || !(obj instanceof SendSmsAdapter)) {
                throw new SendSmsException("绑定的不是有效的适配器，channelConfigId=" + channelConfig.getChannelConfigId());
            }
            adapter = (SendSmsAdapter) obj;
            mapClass2Adapter.put(adapterClass, adapter);
            return adapter;
        } catch (ClassNotFoundException e) {
            throw new SendSmsException("找不到相应的适配器，channelConfigId=" + channelConfig.getChannelConfigId());
        } catch (InstantiationException | IllegalAccessException e) {
            throw new SendSmsException("该适配器类不能够实例化，channelConfigId=" + channelConfig.getChannelConfigId());
        }
    }
    
    /**
     * FIXME 方法注释信息(此标记由Eclipse自动生成,请填写注释信息删除此标记)
     *
     * @param message xx
     * @throws SendSmsException x
     */
    private void throwSendSmsException(String message) throws SendSmsException {
        throw new SendSmsException(message);
    }
    
}
